import { GameObject } from './GameObject';
import { Component } from './Component';

export interface LayerOptions {
    name?: string;
    visible?: boolean;
    active?: boolean;
    zIndex?: number;
}

export class Layer extends GameObject {
    private zIndex: number;
    private objects: Set<GameObject>;
    
    constructor(options: LayerOptions = {}) {
        super(options);
        
        this.zIndex = options.zIndex || 0;
        this.objects = new Set();
    }
    
    /**
     * Add a game object to this layer
     */
    public addObject(object: GameObject): void {
        if (object === this) {
            throw new Error('Cannot add layer to itself');
        }
        
        this.objects.add(object);
        this.addChild(object);
        this.emit('objectAdded', object);
    }
    
    /**
     * Remove a game object from this layer
     */
    public removeObject(object: GameObject): void {
        if (this.objects.has(object)) {
            this.objects.delete(object);
            this.removeChild(object);
            this.emit('objectRemoved', object);
        }
    }
    
    /**
     * Get all objects in this layer
     */
    public getObjects(): GameObject[] {
        return Array.from(this.objects);
    }
    
    /**
     * Get objects of a specific type
     */
    public getObjectsByType<T extends GameObject>(type: new (...args: any[]) => T): T[] {
        return this.getObjects().filter(obj => obj instanceof type) as T[];
    }
    
    /**
     * Get objects with a specific component
     */
    public getObjectsWithComponent<T extends Component>(componentType: new (...args: any[]) => T): GameObject[] {
        return this.getObjects().filter(obj => obj.getComponent(componentType) !== null);
    }
    
    /**
     * Get the z-index of this layer
     */
    public getZIndex(): number {
        return this.zIndex;
    }
    
    /**
     * Set the z-index of this layer
     */
    public setZIndex(zIndex: number): void {
        this.zIndex = zIndex;
        this.emit('zIndexChanged', zIndex);
    }
    
    /**
     * Clear all objects from this layer
     */
    public clear(): void {
        for (const object of this.objects) {
            object.destroy();
        }
        this.objects.clear();
        this.emit('cleared');
    }
    
    /**
     * Find an object by name
     */
    public findObjectByName(name: string): GameObject | null {
        return this.getObjects().find(obj => obj.name === name) || null;
    }
    
    /**
     * Find objects by name (can be multiple)
     */
    public findObjectsByName(name: string): GameObject[] {
        return this.getObjects().filter(obj => obj.name === name);
    }
    
    /**
     * Find an object by ID
     */
    public findObjectById(id: number): GameObject | null {
        return this.getObjects().find(obj => obj.id === id) || null;
    }
    
    /**
     * Check if this layer contains an object
     */
    public hasObject(object: GameObject): boolean {
        return this.objects.has(object);
    }
    
    /**
     * Get the number of objects in this layer
     */
    public getObjectCount(): number {
        return this.objects.size;
    }
    
    /**
     * Sort objects by z-index
     */
    public sortObjects(): void {
        const objects = this.getObjects();
        objects.sort((a, b) => {
            const aZ = a.getComponent(ZIndexComponent)?.zIndex || 0;
            const bZ = b.getComponent(ZIndexComponent)?.zIndex || 0;
            return aZ - bZ;
        });
        
        // Reorder children
        for (const object of objects) {
            this.removeChild(object);
            this.addChild(object);
        }
    }
}

/**
 * Component for managing z-index of game objects
 */
export class ZIndexComponent extends Component {
    private _zIndex: number = 0;
    
    constructor(gameObject: GameObject, zIndex: number = 0) {
        super(gameObject);
        this._zIndex = zIndex;
    }
    
    public get zIndex(): number {
        return this._zIndex;
    }
    
    public set zIndex(value: number) {
        this._zIndex = value;
        const layer = this.gameObject.getParent() as Layer;
        if (layer) {
            layer.sortObjects();
        }
    }
} 